(function () {

    /**
    * GeoKBD 0.3.3 - Georgian keyboard and text convertation library
    *
    * Copyright (c) 2007 Ioseb Dzmanashvili (http://www.code.ge)
    * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
    */

    String.prototype.pasteTo = function (field) {
        field.focus();
        if (document.selection) {
            var range = document.selection.createRange();
            if (range) {
                range.text = this;
            }
        } else if (field.selectionStart != undefined) {
            with (field) {
                var scroll = scrollTop, start = selectionStart, end = selectionEnd;
            }
            var value = field.value.substr(0, start) + this + field.value.substr(end, field.value.length);
            field.value = value;
            field.scrollTop = scroll;
            field.setSelectionRange(start + this.length, start + this.length);
        } else {
            field.value += this;
            field.setSelectionRange(field.value.length, field.value.length);
        }
    };

    String.prototype.translateToKA = function () {

        /**
        * Original idea by Irakli Nadareishvili
        * http://www.sapikhvno.org/viewtopic.php?t=47&postdays=0&postorder=asc&start=10
        */
        var index, chr, text = [], symbols = "abgdevzTiklmnopJrstufqRySCcZwWxjh";

        for (var i = 0; i < this.length; i++) {
            chr = this.substr(i, 1);
            if ((index = symbols.indexOf(chr)) >= 0) {
                text.push(String.fromCharCode(index + 4304));
            } else {
                text.push(chr);
            }
        }

        return text.join('');

    };

    function $(needle, context, lang, switcher, excludeFields) {

        var els = this.els = [];
        context = context || document.forms;

        if (needle && (needle.nodeType || typeof needle == 'object')) {
            this.els.push(needle);
        } else if (!needle || typeof needle == 'string' || excludeFields) {
            (function (query) {

                var tokens = [];

                if (query) {
                    tokens = query.replace(/ /g, '')
						 		  .replace(/\./g, 'className.')
						 		  .replace(/#/g, 'id.')
						 		  .split(',');
                }

                for (var i = 0; i < context.length; i++) {
                    var form = context[i];
                    fn(form).extend(new KAForm());
                    form.init(lang, switcher);
                    for (var j = 0; j < context[i].elements.length; j++) {
                        var el = form.elements[j];
                        if (el.type && /text|textarea/i.test(el.type)) {
                            if (tokens.length) {
                                var fld = false;
                                for (var k = 0; k < tokens.length; k++) {
                                    var tmp = tokens[k].split('.');
                                    if (fld = (tmp.length == 1 && el.name == tmp[0]) || (tmp.length == 2 && el[tmp[0]] == tmp[1])) {
                                        break;
                                    }
                                }
                                if (fld && !excludeFields) {
                                    el.ka = true;
                                    fld = false;
                                } else if (!fld && excludeFields) {
                                    el.ka = true;
                                }
                            } else {
                                el.ka = true;
                            }
                        }
                    }
                }

            })(needle || excludeFields);
        }

        return this;

    }

    $.prototype = {
        each: function (fn) {
            if (fn) {
                for (var i = 0; i < this.els.length; i++) {
                    fn.call(this, this.els[i], i);
                }
            }
            return this;
        },
        extend: function () {
            var p, args = arguments;
            this.each(
				function (target) {
				    for (var i = 0; i < args.length; i++) {
				        if ((p = args[i]) != null) {
				            for (var idx in p) {
				                if (target == p) {
				                    continue;
				                } else if (p[idx] != undefined) {
				                    target[idx] = p[idx];
				                }
				            }
				        }
				    }
				}
			);
            return this;
        },
        get: function (index) {
            return this.els[index || 0] || null;
        }
    };

    function fn(needle, context, lang, switcher, excludeFields) {
        return new $(needle, context, lang, switcher, excludeFields);
    };

    fn.event = function (e) {
        var event = function (orig) {
            this.orig = e;
        };
        event.prototype = {
            getKeyCode: function () {
                return this.keyCode || this.which;
            },
            getTarget: function () {
                return this.target || this.srcElement
            },
            targetIs: function (tagName) {
                var t = this.getTarget();
                t.tagName.toLowerCase() == tagName ? t : null;
            },
            cancel: function () {
                try {
                    e.stopPropagation();
                    e.preventDefault();
                } catch (ex) {
                    e.cancelBubble = true;
                    e.returnValue = false;
                }
            }
        };
        return fn(new event()).extend((e || window.event)).get();
    };

    fn.event.attach = function (o, evt, fnc) {
        if (o.addEventListener) {
            o.addEventListener(evt, fnc, false);
        } else if (o.attachEvent) {
            return o.attachEvent('on' + evt, fnc);
        } else {
            o['on' + evt] = fnc;
        }
    };

    fn.event.detach = function (o, evt, fnc) {
        if (o.removeEventListener) {
            o.removeEventListener(evt, fnc, false);
        } else if (o.detachEvent) {
            o.detachEvent('on' + evt, fnc);
        } else {
            o['on' + evt] = null;
        }
    };

    var ua = navigator.userAgent.toLowerCase();
    fn.browser = {
        is: function (key) { ua.indexOf(key) > -1 },
        isOpera: ua.indexOf('opera') > -1,
        isIe: ua.indexOf('msie') > -1,
        isIe6: ua.indexOf('msie 6') > -1,
        isIe7: ua.indexOf('msie 7') > -1
    };

    var KAForm = function () {
        this.ka = true;
        this.switcher = null;
        this.global = true;
        this.init = function (lang, switcher) {
            this.global = !!!lang;
            if (!this.global) {
                lang = lang || GeoKBD.KA;
            } else {
                lang = lang || GeoKBD.lang;
            }
            switcher = switcher || 'geo';
            if (this.elements[switcher]) {
                this.switcher = this.elements[switcher];
                this.global = false;
            } else {
                this.switcher = {};
                this.switcher.checked = lang == GeoKBD.KA;
            }
            this.ka = this.switcher.checked;
        },
		this.changeLang = function (flag) {
		    if (this.switcher) {
		        if (flag == undefined) {
		            this.switcher.checked = flag = !this.switcher.checked;
		        } else {
		            this.switcher.checked = flag;
		        }
		    }
		    this.ka = !!flag;
		},
		this.onkeypress = function (e) {

		    e = fn.event(e);

		    if (e.altKey || e.ctrlKey || e.metaKey) return;

		    if (!fn.browser.isIe && !fn.browser.isOpera && !e.charCode) {
		        return;
		    }

		    var target = e.getTarget();

		    if (target.ka != undefined && target.ka) {

		        var keyCode = e.getKeyCode();
		        if (keyCode == 96) {
		            this.changeLang();
		            return false;
		        }

		        if (!this.switcher.checked) return;

		        var text = String.fromCharCode(keyCode);
		        var kaText = text.translateToKA();

		        if (kaText != text) {
		            if (fn.browser.isIe) {
		                window.event.keyCode = kaText.charCodeAt(0);
		            } else {
		                kaText.pasteTo(target);
		                return false;
		            }
		        }

		    }

		}
    };

    var GeoKBD = {
        KA: 'ka',
        EN: 'en',
        lang: 'ka',
        setGlobalLanguage: function (lang, switcher) {
            this.lang = lang;
            switcher = document.getElementById(switcher) || {};
            if (!switcher.nodeType) {
                switcher.checked = lang == GeoKBD.KA;
            }
            document.body.onkeypress = function (e) {
                if (fn.event(e).getKeyCode() == 96) {
                    switcher.checked = !switcher.checked;
                    for (var i = 0; i < document.forms.length; i++) {
                        var form = document.forms[i];
                        if (form.global && form.changeLang) {
                            form.changeLang(switcher.checked);
                        }
                    }
                }
            }
        },
        pasteSelection: function (field) {
            try {
                var selection = document.selection ? document.selection.createRange().text : document.getSelection();
                selection.pasteTo(field);
            } catch (e) { }
            return false;
        },
        map: function (params) {

            var form, fields, switcher, lang, excludeFields;
            if (params) {
                form = params.forms || null;
                fields = params.fields || null;
                switcher = params.switcher || null;
                lang = params.lang || null;
                excludeFields = params.excludeFields || null;
            }
            var names = [], forms = [];

            if (form) {
                if (form.constructor) {
                    if (form.constructor == String) {
                        names.push(form);
                    } else if (form.constructor == Array) {
                        names = form;
                    }
                    if (names.length) {
                        for (var idx in names) {
                            if (document.forms[names[idx]]) {
                                forms.push(document.forms[names[idx]]);
                            }
                        }
                    }
                } else {
                    forms.push(form);
                }
            } else {
                forms = document.forms;
            }

            if (fields) {
                fields = typeof fields == 'string' ? fields : fields.join(',');
            }

            if (excludeFields) {
                excludeFields = typeof excludeFields == 'string' ? excludeFields : excludeFields.join(',');
            }

            fn(fields, forms, lang, switcher, excludeFields);

        },

        mapForm: function (form, fields, lang, switcher) {
            this.map(
				{
				    forms: form,
				    fields: fields,
				    switcher: switcher,
				    lang: lang
				}
			);
        },

        mapFields: function (fields, lang) {
            this.map(null, fields, null, lang);
        },

        mapIFrame: function (iframe) {

            var __keypress = function (e) {
                e = fn.event(e);
                if (e.altKey || e.ctrlKey) return;
                var doc = e.getTarget().ownerDocument;
                if (doc.ka == undefined) doc.ka = true;
                var keyCode = e.getKeyCode();
                var text = String.fromCharCode(keyCode);
                var form, switcher;
                if (parent.document.forms[doc.parentForm]) {
                    form = parent.document.forms[doc.parentForm];
                    if (form.switcher && form[form.switcher]) {
                        switcher = form[form.switcher];
                    }
                }

                if (keyCode == 96) {
                    doc.ka = !doc.ka;
                    if (switcher) switcher.checked = doc.ka;
                    e.cancel();
                } else if (switcher) {
                    doc.ka = switcher.checked;
                }

                form = switcher = null;

                if (doc.ka) {
                    var kaText = text.translateToKA();
                    if (kaText != text) {
                        if (!fn.browser.isIe) {
                            doc.execCommand('InsertHTML', false, kaText);
                        } else {
                            var range = doc.selection.createRange();
                            range.pasteHTML(kaText);
                        }
                        e.cancel();
                    }
                }

            }

            var __focus = function (e) {
                fn.event.attach(this.contentWindow.document, 'keypress', __keypress);
                this.onfocus = null;
            };

            var interval = window.setInterval(function () {
                var el = typeof iframe == 'string' ? document.getElementById(iframe) : iframe();
                if (el) {
                    for (var p = el.parentNode; p && p != document.body; p = p.parentNode) {
                        if (/form/i.test(p.tagName)) {
                            if (el.contentWindow.document) {
                                el.contentWindow.document.parentForm = p.name || p.id;
                            } else {
                                el.document.parentForm = p.name || p.id;
                            }
                            break;
                        }
                    }
                    if (!fn.browser.isIe) {
                        fn.event.attach(el.contentWindow.document, 'keypress', __keypress, true);
                    } else {
                        el.onfocus = __focus;
                    }
                    el = null;
                    window.clearInterval(interval);
                }
            }, 0);

        },

        ready: function (fn) {
            var load = window.onload;
            if (!window.onload) {
                window.onload = fn;
            } else {
                window.onload = function () {
                    load(); fn();
                };
            }
        }

    };

    window.GeoKBD = GeoKBD;

})();